crypto/internal/hpke: modularize API and support more ciphersuites

Updates #75300

Change-Id: I6a6a6964de449b36bc6f5594e08c3c47a0a2f17f
Reviewed-on: https://go-review.googlesource.com/c/go/+/701435
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Mark Freeman <markfreeman@google.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
This commit is contained in:
Filippo Valsorda 2025-09-06 21:37:09 +02:00 committed by Gopher Robot
parent e7d47ac33d
commit 7c985a2df4
12 changed files with 1301 additions and 484 deletions

View file

@ -9,24 +9,11 @@ import (
"crypto/internal/hpke"
"errors"
"fmt"
"slices"
"strings"
"golang.org/x/crypto/cryptobyte"
)
// sortedSupportedAEADs is just a sorted version of hpke.SupportedAEADS.
// We need this so that when we insert them into ECHConfigs the ordering
// is stable.
var sortedSupportedAEADs []uint16
func init() {
for aeadID := range hpke.SupportedAEADs {
sortedSupportedAEADs = append(sortedSupportedAEADs, aeadID)
}
slices.Sort(sortedSupportedAEADs)
}
type echCipher struct {
KDFID uint16
AEADID uint16
@ -162,25 +149,8 @@ func parseECHConfigList(data []byte) ([]echConfig, error) {
return configs, nil
}
func pickECHConfig(list []echConfig) *echConfig {
func pickECHConfig(list []echConfig) (*echConfig, hpke.KEMSender, hpke.KDF, hpke.AEAD) {
for _, ec := range list {
if _, ok := hpke.SupportedKEMs[ec.KemID]; !ok {
continue
}
var validSCS bool
for _, cs := range ec.SymmetricCipherSuite {
if _, ok := hpke.SupportedAEADs[cs.AEADID]; !ok {
continue
}
if _, ok := hpke.SupportedKDFs[cs.KDFID]; !ok {
continue
}
validSCS = true
break
}
if !validSCS {
continue
}
if !validDNSName(string(ec.PublicName)) {
continue
}
@ -196,25 +166,26 @@ func pickECHConfig(list []echConfig) *echConfig {
if unsupportedExt {
continue
}
return &ec
}
return nil
}
func pickECHCipherSuite(suites []echCipher) (echCipher, error) {
for _, s := range suites {
// NOTE: all of the supported AEADs and KDFs are fine, rather than
// imposing some sort of preference here, we just pick the first valid
// suite.
if _, ok := hpke.SupportedAEADs[s.AEADID]; !ok {
s, err := hpke.NewKEMSender(ec.KemID, ec.PublicKey)
if err != nil {
continue
}
if _, ok := hpke.SupportedKDFs[s.KDFID]; !ok {
continue
for _, cs := range ec.SymmetricCipherSuite {
// All of the supported AEADs and KDFs are fine, rather than
// imposing some sort of preference here, we just pick the first
// valid suite.
kdf, err := hpke.NewKDF(cs.KDFID)
if err != nil {
continue
}
aead, err := hpke.NewAEAD(cs.AEADID)
if err != nil {
continue
}
return &ec, s, kdf, aead
}
return s, nil
}
return echCipher{}, errors.New("tls: no supported symmetric ciphersuites for ECH")
return nil, nil, nil, nil
}
func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
@ -592,18 +563,28 @@ func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedC
skip, config, err := parseECHConfig(echKey.Config)
if err != nil || skip {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys Config: %s", err)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config: %s", err)
}
if skip {
continue
}
echPriv, err := hpke.ParseHPKEPrivateKey(config.KemID, echKey.PrivateKey)
echPriv, err := hpke.NewKEMRecipient(config.KemID, echKey.PrivateKey)
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys PrivateKey: %s", err)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey PrivateKey: %s", err)
}
kdf, err := hpke.NewKDF(echCiphersuite.KDFID)
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config KDF: %s", err)
}
aead, err := hpke.NewAEAD(echCiphersuite.AEADID)
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config AEAD: %s", err)
}
info := append([]byte("tls ech\x00"), echKey.Config...)
hpkeContext, err := hpke.SetupRecipient(hpke.DHKEM_X25519_HKDF_SHA256, echCiphersuite.KDFID, echCiphersuite.AEADID, echPriv, info, encap)
hpkeContext, err := hpke.NewRecipient(encap, echPriv, kdf, aead, info)
if err != nil {
// attempt next trial decryption
continue