github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/common/tls/ech_keygen.go (about)

     1  //go:build with_ech
     2  
     3  package tls
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"encoding/pem"
     9  
    10  	cftls "github.com/sagernet/cloudflare-tls"
    11  	E "github.com/sagernet/sing/common/exceptions"
    12  
    13  	"github.com/cloudflare/circl/hpke"
    14  	"github.com/cloudflare/circl/kem"
    15  )
    16  
    17  func ECHKeygenDefault(serverName string, pqSignatureSchemesEnabled bool) (configPem string, keyPem string, err error) {
    18  	cipherSuites := []echCipherSuite{
    19  		{
    20  			kdf:  hpke.KDF_HKDF_SHA256,
    21  			aead: hpke.AEAD_AES128GCM,
    22  		}, {
    23  			kdf:  hpke.KDF_HKDF_SHA256,
    24  			aead: hpke.AEAD_ChaCha20Poly1305,
    25  		},
    26  	}
    27  
    28  	keyConfig := []myECHKeyConfig{
    29  		{id: 0, kem: hpke.KEM_X25519_HKDF_SHA256},
    30  	}
    31  	if pqSignatureSchemesEnabled {
    32  		keyConfig = append(keyConfig, myECHKeyConfig{id: 1, kem: hpke.KEM_X25519_KYBER768_DRAFT00})
    33  	}
    34  
    35  	keyPairs, err := echKeygen(0xfe0d, serverName, keyConfig, cipherSuites)
    36  	if err != nil {
    37  		return
    38  	}
    39  
    40  	var configBuffer bytes.Buffer
    41  	var totalLen uint16
    42  	for _, keyPair := range keyPairs {
    43  		totalLen += uint16(len(keyPair.rawConf))
    44  	}
    45  	binary.Write(&configBuffer, binary.BigEndian, totalLen)
    46  	for _, keyPair := range keyPairs {
    47  		configBuffer.Write(keyPair.rawConf)
    48  	}
    49  
    50  	var keyBuffer bytes.Buffer
    51  	for _, keyPair := range keyPairs {
    52  		keyBuffer.Write(keyPair.rawKey)
    53  	}
    54  
    55  	configPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer.Bytes()}))
    56  	keyPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer.Bytes()}))
    57  	return
    58  }
    59  
    60  type echKeyConfigPair struct {
    61  	id      uint8
    62  	key     cftls.EXP_ECHKey
    63  	rawKey  []byte
    64  	conf    myECHKeyConfig
    65  	rawConf []byte
    66  }
    67  
    68  type echCipherSuite struct {
    69  	kdf  hpke.KDF
    70  	aead hpke.AEAD
    71  }
    72  
    73  type myECHKeyConfig struct {
    74  	id   uint8
    75  	kem  hpke.KEM
    76  	seed []byte
    77  }
    78  
    79  func echKeygen(version uint16, serverName string, conf []myECHKeyConfig, suite []echCipherSuite) ([]echKeyConfigPair, error) {
    80  	be := binary.BigEndian
    81  	// prepare for future update
    82  	if version != 0xfe0d {
    83  		return nil, E.New("unsupported ECH version", version)
    84  	}
    85  
    86  	suiteBuf := make([]byte, 0, len(suite)*4+2)
    87  	suiteBuf = be.AppendUint16(suiteBuf, uint16(len(suite))*4)
    88  	for _, s := range suite {
    89  		if !s.kdf.IsValid() || !s.aead.IsValid() {
    90  			return nil, E.New("invalid HPKE cipher suite")
    91  		}
    92  		suiteBuf = be.AppendUint16(suiteBuf, uint16(s.kdf))
    93  		suiteBuf = be.AppendUint16(suiteBuf, uint16(s.aead))
    94  	}
    95  
    96  	pairs := []echKeyConfigPair{}
    97  	for _, c := range conf {
    98  		pair := echKeyConfigPair{}
    99  		pair.id = c.id
   100  		pair.conf = c
   101  
   102  		if !c.kem.IsValid() {
   103  			return nil, E.New("invalid HPKE KEM")
   104  		}
   105  
   106  		kpGenerator := c.kem.Scheme().GenerateKeyPair
   107  		if len(c.seed) > 0 {
   108  			kpGenerator = func() (kem.PublicKey, kem.PrivateKey, error) {
   109  				pub, sec := c.kem.Scheme().DeriveKeyPair(c.seed)
   110  				return pub, sec, nil
   111  			}
   112  			if len(c.seed) < c.kem.Scheme().PrivateKeySize() {
   113  				return nil, E.New("HPKE KEM seed too short")
   114  			}
   115  		}
   116  
   117  		pub, sec, err := kpGenerator()
   118  		if err != nil {
   119  			return nil, E.Cause(err, "generate ECH config key pair")
   120  		}
   121  		b := []byte{}
   122  		b = be.AppendUint16(b, version)
   123  		b = be.AppendUint16(b, 0) // length field
   124  		// contents
   125  		// key config
   126  		b = append(b, c.id)
   127  		b = be.AppendUint16(b, uint16(c.kem))
   128  		pubBuf, err := pub.MarshalBinary()
   129  		if err != nil {
   130  			return nil, E.Cause(err, "serialize ECH public key")
   131  		}
   132  		b = be.AppendUint16(b, uint16(len(pubBuf)))
   133  		b = append(b, pubBuf...)
   134  
   135  		b = append(b, suiteBuf...)
   136  		// end key config
   137  		// max name len, not supported
   138  		b = append(b, 0)
   139  		// server name
   140  		b = append(b, byte(len(serverName)))
   141  		b = append(b, []byte(serverName)...)
   142  		// extensions, not supported
   143  		b = be.AppendUint16(b, 0)
   144  
   145  		be.PutUint16(b[2:], uint16(len(b)-4))
   146  
   147  		pair.rawConf = b
   148  
   149  		secBuf, err := sec.MarshalBinary()
   150  		sk := []byte{}
   151  		sk = be.AppendUint16(sk, uint16(len(secBuf)))
   152  		sk = append(sk, secBuf...)
   153  		sk = be.AppendUint16(sk, uint16(len(b)))
   154  		sk = append(sk, b...)
   155  
   156  		cfECHKeys, err := cftls.EXP_UnmarshalECHKeys(sk)
   157  		if err != nil {
   158  			return nil, E.Cause(err, "bug: can't parse generated ECH server key")
   159  		}
   160  		if len(cfECHKeys) != 1 {
   161  			return nil, E.New("bug: unexpected server key count")
   162  		}
   163  		pair.key = cfECHKeys[0]
   164  		pair.rawKey = sk
   165  
   166  		pairs = append(pairs, pair)
   167  	}
   168  	return pairs, nil
   169  }