client-hello-mirror/clienthello/named_groups.go

133 lines
2.8 KiB
Go

// SPDX-FileCopyrightText: 2022-2023 nervuri <https://nervuri.net/contact>
//
// SPDX-License-Identifier: BSD-3-Clause
package clienthello
import (
_ "embed"
"encoding/csv"
"fmt"
"io"
"log"
"strconv"
"strings"
)
// RFC 8446, Section 4.2.7
// https://www.rfc-editor.org/rfc/rfc8446.html#section-4.2.7
// https://www.iana.org/assignments/tls-parameters/tls-parameters-8.csv
//
//go:embed named-groups.csv
var namedGroupsCSV string
var NamedGroupList = parseNamedGroupsCSV()
type NamedGroup any // (uint16 | NamedGroupInfo)
type NamedGroupInfo struct {
Code uint16 `json:"code"`
HexCode string `json:"hex_code"`
Name string `json:"name"`
Recommended bool `json:"-"`
Reference string `json:"-"`
}
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
type KeyShare struct {
Group NamedGroup `json:"group"`
Data byteSlice `json:"data"`
}
func parseNamedGroupsCSV() map[uint16]NamedGroupInfo {
namedGroups := map[uint16]NamedGroupInfo{}
r := csv.NewReader(strings.NewReader(namedGroupsCSV))
// Skip CSV header.
_, err := r.Read()
if err != nil {
log.Fatal(err)
}
for {
row, err := r.Read()
if err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
var (
val = row[0]
desc = row[1]
rec = row[3]
ref = row[4]
)
if desc == "Unassigned" {
continue
}
if strings.HasPrefix(desc, "Reserved") && ref != "[RFC8701]" {
continue
}
code, err := strconv.Atoi(val)
if err != nil {
log.Println(err)
}
group := NamedGroupInfo{
Code: uint16(code),
HexCode: fmt.Sprintf("%04X", code),
Name: desc,
Reference: ref,
}
if rec == "Y" {
group.Recommended = true
}
if ref == "[RFC8701]" {
group.Name = "GREASE"
}
namedGroups[group.Code] = group
}
return namedGroups
}
func GetNamedGroupInfo(namedGroupCode uint16, mustName bool) NamedGroupInfo {
info, found := NamedGroupList[namedGroupCode]
if !found {
info = NamedGroupInfo{
Code: namedGroupCode,
HexCode: fmt.Sprintf("%04X", namedGroupCode),
}
}
if mustName && info.Name == "" {
info.Name = "0x" + info.HexCode
}
return info
}
func (m *ClientHelloMsg) AddNamedGroupInfo() {
for i, ext := range m.Extensions {
switch ext.Code {
case extensionSupportedGroups:
for j, group := range ext.Data.SupportedGroups {
m.Extensions[i].Data.SupportedGroups[j] =
GetNamedGroupInfo(group.(uint16), false)
}
case extensionKeyShare:
for j, kShare := range ext.Data.KeyShares {
m.Extensions[i].Data.KeyShares[j].Group =
GetNamedGroupInfo(kShare.Group.(uint16), false)
}
}
}
}
func (m *ClientHelloMsg) GetSupportedGroups() []NamedGroup {
var supportedGroups []NamedGroup
for _, ext := range m.Extensions {
if ext.Code == extensionSupportedGroups {
supportedGroups = append(supportedGroups, ext.Data.SupportedGroups...)
}
}
return supportedGroups
}