189 lines
5.4 KiB
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
|
|
}
|