123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- package tls
- import (
- "bytes"
- "encoding/binary"
- "encoding/pem"
- E "github.com/sagernet/sing/common/exceptions"
- "github.com/cloudflare/circl/hpke"
- "github.com/cloudflare/circl/kem"
- )
- func ECHKeygenDefault(serverName string) (configPem string, keyPem string, err error) {
- cipherSuites := []echCipherSuite{
- {
- kdf: hpke.KDF_HKDF_SHA256,
- aead: hpke.AEAD_AES128GCM,
- }, {
- kdf: hpke.KDF_HKDF_SHA256,
- aead: hpke.AEAD_ChaCha20Poly1305,
- },
- }
- keyConfig := []myECHKeyConfig{
- {id: 0, kem: hpke.KEM_X25519_HKDF_SHA256},
- }
- keyPairs, err := echKeygen(0xfe0d, serverName, keyConfig, cipherSuites)
- if err != nil {
- return
- }
- var configBuffer bytes.Buffer
- var totalLen uint16
- for _, keyPair := range keyPairs {
- totalLen += uint16(len(keyPair.rawConf))
- }
- binary.Write(&configBuffer, binary.BigEndian, totalLen)
- for _, keyPair := range keyPairs {
- configBuffer.Write(keyPair.rawConf)
- }
- var keyBuffer bytes.Buffer
- for _, keyPair := range keyPairs {
- keyBuffer.Write(keyPair.rawKey)
- }
- configPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer.Bytes()}))
- keyPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer.Bytes()}))
- return
- }
- type echKeyConfigPair struct {
- id uint8
- rawKey []byte
- conf myECHKeyConfig
- rawConf []byte
- }
- type echCipherSuite struct {
- kdf hpke.KDF
- aead hpke.AEAD
- }
- type myECHKeyConfig struct {
- id uint8
- kem hpke.KEM
- seed []byte
- }
- func echKeygen(version uint16, serverName string, conf []myECHKeyConfig, suite []echCipherSuite) ([]echKeyConfigPair, error) {
- be := binary.BigEndian
- // prepare for future update
- if version != 0xfe0d {
- return nil, E.New("unsupported ECH version", version)
- }
- suiteBuf := make([]byte, 0, len(suite)*4+2)
- suiteBuf = be.AppendUint16(suiteBuf, uint16(len(suite))*4)
- for _, s := range suite {
- if !s.kdf.IsValid() || !s.aead.IsValid() {
- return nil, E.New("invalid HPKE cipher suite")
- }
- suiteBuf = be.AppendUint16(suiteBuf, uint16(s.kdf))
- suiteBuf = be.AppendUint16(suiteBuf, uint16(s.aead))
- }
- pairs := []echKeyConfigPair{}
- for _, c := range conf {
- pair := echKeyConfigPair{}
- pair.id = c.id
- pair.conf = c
- if !c.kem.IsValid() {
- return nil, E.New("invalid HPKE KEM")
- }
- kpGenerator := c.kem.Scheme().GenerateKeyPair
- if len(c.seed) > 0 {
- kpGenerator = func() (kem.PublicKey, kem.PrivateKey, error) {
- pub, sec := c.kem.Scheme().DeriveKeyPair(c.seed)
- return pub, sec, nil
- }
- if len(c.seed) < c.kem.Scheme().PrivateKeySize() {
- return nil, E.New("HPKE KEM seed too short")
- }
- }
- pub, sec, err := kpGenerator()
- if err != nil {
- return nil, E.Cause(err, "generate ECH config key pair")
- }
- b := []byte{}
- b = be.AppendUint16(b, version)
- b = be.AppendUint16(b, 0) // length field
- // contents
- // key config
- b = append(b, c.id)
- b = be.AppendUint16(b, uint16(c.kem))
- pubBuf, err := pub.MarshalBinary()
- if err != nil {
- return nil, E.Cause(err, "serialize ECH public key")
- }
- b = be.AppendUint16(b, uint16(len(pubBuf)))
- b = append(b, pubBuf...)
- b = append(b, suiteBuf...)
- // end key config
- // max name len, not supported
- b = append(b, 0)
- // server name
- b = append(b, byte(len(serverName)))
- b = append(b, []byte(serverName)...)
- // extensions, not supported
- b = be.AppendUint16(b, 0)
- be.PutUint16(b[2:], uint16(len(b)-4))
- pair.rawConf = b
- secBuf, err := sec.MarshalBinary()
- if err != nil {
- return nil, E.Cause(err, "serialize ECH private key")
- }
- sk := []byte{}
- sk = be.AppendUint16(sk, uint16(len(secBuf)))
- sk = append(sk, secBuf...)
- sk = be.AppendUint16(sk, uint16(len(b)))
- sk = append(sk, b...)
- pair.rawKey = sk
- pairs = append(pairs, pair)
- }
- return pairs, nil
- }
|