client-hello-mirror/clienthello/signature_schemes.go

189 lines
5.4 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.3
// https://www.rfc-editor.org/rfc/rfc8446.html#section-4.2.3
// https://www.iana.org/assignments/tls-parameters/tls-signaturescheme.csv
//
//go:embed signature-schemes.csv
var signatureSchemesCSV string
var SignatureSchemeList = parseSignatureSchemesCSV()
type SignatureScheme any // (uint16 | SignatureSchemeInfo)
type SignatureSchemeInfo struct {
Code uint16 `json:"code"`
HexCode string `json:"hex_code"`
Name string `json:"name"`
Recommended bool `json:"recommended"`
Reference string `json:"-"`
}
// https://www.rfc-editor.org/rfc/rfc5246.html#section-7.4.1.4.1
type oldAlgo struct {
Code uint8
Name string
Reference string
}
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
var oldHashAlgoList = map[uint8]oldAlgo{
0: {Code: 0, Name: "none", Reference: "[RFC5246]"},
1: {Code: 1, Name: "md5", Reference: "[RFC5246]"},
2: {Code: 2, Name: "sha1", Reference: "[RFC5246]"},
3: {Code: 3, Name: "sha224", Reference: "[RFC5246]"},
4: {Code: 4, Name: "sha256", Reference: "[RFC5246]"},
5: {Code: 5, Name: "sha384", Reference: "[RFC5246]"},
6: {Code: 6, Name: "sha512", Reference: "[RFC5246]"},
8: {Code: 8, Name: "", Reference: "[RFC8422]"}, // Intrinsic
}
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
var oldSigAlgoList = map[uint8]oldAlgo{
0: {Code: 0, Name: "anonymous", Reference: "[RFC5246]"},
1: {Code: 1, Name: "rsa", Reference: "[RFC5246]"},
2: {Code: 2, Name: "dsa", Reference: "[RFC5246]"},
3: {Code: 3, Name: "ecdsa", Reference: "[RFC5246]"},
7: {Code: 7, Name: "ed25519", Reference: "[RFC8422]"},
8: {Code: 8, Name: "ed448", Reference: "[RFC8422]"},
64: {Code: 64, Name: "gostr34102012_256", Reference: "[RFC9189]"},
65: {Code: 65, Name: "gostr34102012_512", Reference: "[RFC9189]"},
}
func parseSignatureSchemesCSV() map[uint16]SignatureSchemeInfo {
signatureSchemes := map[uint16]SignatureSchemeInfo{}
r := csv.NewReader(strings.NewReader(signatureSchemesCSV))
// 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[2]
ref = row[3]
)
if desc == "Unassigned" {
continue
}
if strings.HasPrefix(desc, "Reserved") && ref != "[RFC8701]" {
continue
}
val = strings.Replace(val, "0x", "", 1)
code, err := strconv.ParseUint(val, 16, 16)
if err != nil {
log.Println(err)
}
sigScheme := SignatureSchemeInfo{
Code: uint16(code),
HexCode: fmt.Sprintf("%04X", code),
Name: desc,
Reference: ref,
}
if rec == "Y" {
sigScheme.Recommended = true
}
if ref == "[RFC8701]" {
// Note: as of September 2023, the IANA signature scheme registry
// doesn't include GREASE values.
sigScheme.Name = "GREASE"
}
signatureSchemes[sigScheme.Code] = sigScheme
}
return signatureSchemes
}
func GetSignatureSchemeInfo(sigSchemeCode uint16, mustName bool) SignatureSchemeInfo {
info, found := SignatureSchemeList[sigSchemeCode]
if !found {
info = SignatureSchemeInfo{
Code: sigSchemeCode,
HexCode: fmt.Sprintf("%04X", sigSchemeCode),
}
if isGREASE16(sigSchemeCode) {
// As of September 2023, the IANA signature scheme registry
// doesn't include GREASE values, so we need to check here.
info.Name = "GREASE"
info.Reference = "[RFC8701]"
} else {
// If info for this code was not found in the signature schemes
// CSV file, then we might be dealing with values which were
// removed in TLSv1.3, so we can try to derive the name by
// concatenating hash and signature algorithm names.
var hashAlgoCode = uint8(sigSchemeCode >> 8) // first octet
var sigAlgoCode = uint8(sigSchemeCode) // second octet
// info.Name will be empty if either hashAlgoCode or sigAlgoCode
// is not known.
hashInfo, found := oldHashAlgoList[hashAlgoCode]
if found {
info.Name = hashInfo.Name
if info.Name != "" {
info.Name += ","
}
sigInfo, found := oldSigAlgoList[sigAlgoCode]
if found {
info.Name += sigInfo.Name
} else {
info.Name = ""
}
}
info.Reference = oldHashAlgoList[hashAlgoCode].Reference
if info.Reference != oldSigAlgoList[sigAlgoCode].Reference {
info.Reference += oldSigAlgoList[sigAlgoCode].Reference
}
}
}
if mustName && info.Name == "" {
info.Name = "0x" + info.HexCode
}
return info
}
func (m *ClientHelloMsg) AddSignatureSchemeInfo() {
for i, ext := range m.Extensions {
if ext.Code == extensionSignatureAlgorithms ||
ext.Code == extensionSignatureAlgorithmsCert {
for j, sigAlg := range ext.Data.SupportedSignatureAlgorithms {
m.Extensions[i].Data.SupportedSignatureAlgorithms[j] =
GetSignatureSchemeInfo(sigAlg.(uint16), false)
}
}
}
}
func (m *ClientHelloMsg) GetSignatureSchemes() []SignatureScheme {
var supportedSchemes []SignatureScheme
for _, ext := range m.Extensions {
if ext.Code == extensionSignatureAlgorithms {
supportedSchemes = append(supportedSchemes,
ext.Data.SupportedSignatureAlgorithms...)
}
}
return supportedSchemes
}