fix: ech with hrr

This commit is contained in:
Mingye Chen 2025-03-20 16:28:22 -06:00
parent 9b2ccad920
commit f6f3c9867d
3 changed files with 105 additions and 71 deletions

View file

@ -76,18 +76,14 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
if hs.echContext != nil {
hs.echContext.innerTranscript = hs.suite.hash.New()
// [uTLS SECTION BEGIN]
encodedInner, err := encodeInnerClientHelloReorderOuterExts(hs.echContext.innerHello, int(hs.echContext.config.MaxNameLength), hs.uconn.extensionsList())
if err != nil {
return err
}
decodedInner, err := decodeInnerClientHello(hs.hello, encodedInner)
if err != nil {
return err
}
if err := transcriptMsg(decodedInner, hs.echContext.innerTranscript); err != nil {
return err
if hs.uconn != nil && hs.uconn.clientHelloBuildStatus == BuildByUtls {
if err := hs.uconn.echTranscriptMsg(hs.hello, hs.echContext); err != nil {
return err
}
} else {
if err := transcriptMsg(hs.echContext.innerHello, hs.echContext.innerTranscript); err != nil {
return err
}
}
// [uTLS SECTION END]
}
@ -438,7 +434,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
hs.uconn.Extensions[cookieIndex:]...)...)
}
}
if err := hs.uconn.MarshalClientHello(); err != nil {
if err := hs.uconn.MarshalClientHelloNoECH(); err != nil {
return err
}
hs.hello.original = hs.uconn.HandshakeState.Hello.Raw
@ -457,12 +453,24 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
// extension which may have changed is keyShares.
hs.hello.keyShares = hello.keyShares
hs.echContext.innerHello = hello
if err := transcriptMsg(hs.echContext.innerHello, hs.echContext.innerTranscript); err != nil {
return err
}
if hs.uconn != nil && hs.uconn.clientHelloBuildStatus == BuildByUtls {
if err := hs.uconn.computeAndUpdateOuterECHExtension(hs.echContext.innerHello, hs.echContext, false); err != nil {
return err
}
if err := computeAndUpdateOuterECHExtension(hs.hello, hs.echContext.innerHello, hs.echContext, false); err != nil {
return err
if err := hs.uconn.echTranscriptMsg(hs.hello, hs.echContext); err != nil {
return err
}
hs.hello.original = hs.uconn.HandshakeState.Hello.Raw
} else {
if err := transcriptMsg(hs.echContext.innerHello, hs.echContext.innerTranscript); err != nil {
return err
}
if err := computeAndUpdateOuterECHExtension(hs.hello, hs.echContext.innerHello, hs.echContext, false); err != nil {
return err
}
}
} else {
hs.hello = hello

114
u_conn.go
View file

@ -503,6 +503,66 @@ func (uconn *UConn) extensionsList() []uint16 {
return outerExts
}
func (uconn *UConn) computeAndUpdateOuterECHExtension(inner *clientHelloMsg, ech *echClientContext, useKey bool) error {
var encapKey []byte
if useKey {
encapKey = ech.encapsulatedKey
}
encodedInner, err := encodeInnerClientHelloReorderOuterExts(inner, int(ech.config.MaxNameLength), uconn.extensionsList())
if err != nil {
return err
}
// NOTE: the tag lengths for all of the supported AEADs are the same (16
// bytes), so we have hardcoded it here. If we add support for another AEAD
// with a different tag length, we will need to change this.
encryptedLen := len(encodedInner) + 16 // AEAD tag length
outerECHExt, err := generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, make([]byte, encryptedLen))
if err != nil {
return err
}
echExtIdx := slices.IndexFunc(uconn.Extensions, func(ext TLSExtension) bool {
_, ok := ext.(EncryptedClientHelloExtension)
return ok
})
oldExt := uconn.Extensions[echExtIdx]
uconn.Extensions[echExtIdx] = &GenericExtension{
Id: extensionEncryptedClientHello,
Data: outerECHExt,
}
if err := uconn.MarshalClientHelloNoECH(); err != nil {
return err
}
serializedOuter := uconn.HandshakeState.Hello.Raw
serializedOuter = serializedOuter[4:] // strip the four byte prefix
encryptedInner, err := ech.hpkeContext.Seal(serializedOuter, encodedInner)
if err != nil {
return err
}
outerECHExt, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, encryptedInner)
if err != nil {
return err
}
uconn.Extensions[echExtIdx] = &GenericExtension{
Id: extensionEncryptedClientHello,
Data: outerECHExt,
}
if err := uconn.MarshalClientHelloNoECH(); err != nil {
return err
}
uconn.Extensions[echExtIdx] = oldExt
return nil
}
func (uconn *UConn) MarshalClientHello() error {
if len(uconn.config.EncryptedClientHelloConfigList) > 0 {
inner, _, ech, err := uconn.makeClientHello()
@ -518,22 +578,6 @@ func (uconn *UConn) MarshalClientHello() error {
ech.innerHello = inner
encapKey := ech.encapsulatedKey
encodedInner, err := encodeInnerClientHelloReorderOuterExts(inner, int(ech.config.MaxNameLength), uconn.extensionsList())
if err != nil {
return err
}
// NOTE: the tag lengths for all of the supported AEADs are the same (16
// bytes), so we have hardcoded it here. If we add support for another AEAD
// with a different tag length, we will need to change this.
encryptedLen := len(encodedInner) + 16 // AEAD tag length
outerECHExt, err := generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, make([]byte, encryptedLen))
if err != nil {
return err
}
sniExtIdex := slices.IndexFunc(uconn.Extensions, func(ext TLSExtension) bool {
_, ok := ext.(*SNIExtension)
return ok
@ -542,43 +586,7 @@ func (uconn *UConn) MarshalClientHello() error {
ServerName: string(ech.config.PublicName),
}
echExtIdx := slices.IndexFunc(uconn.Extensions, func(ext TLSExtension) bool {
_, ok := ext.(EncryptedClientHelloExtension)
return ok
})
uconn.Extensions[echExtIdx] = &GenericExtension{
Id: extensionEncryptedClientHello,
Data: outerECHExt,
}
// uconn.HandshakeState.Hello.Random = make([]byte, 32)
// _, err = io.ReadFull(uconn.config.rand(), uconn.HandshakeState.Hello.Random)
// if err != nil {
// return errors.New("tls: short read from Rand: " + err.Error())
// }
if err := uconn.MarshalClientHelloNoECH(); err != nil {
return err
}
serializedOuter := uconn.HandshakeState.Hello.Raw
serializedOuter = serializedOuter[4:] // strip the four byte prefix
encryptedInner, err := ech.hpkeContext.Seal(serializedOuter, encodedInner)
if err != nil {
return err
}
outerECHExt, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, encryptedInner)
if err != nil {
return err
}
uconn.Extensions[echExtIdx] = &GenericExtension{
Id: extensionEncryptedClientHello,
Data: outerECHExt,
}
if err := uconn.MarshalClientHelloNoECH(); err != nil {
return err
}
uconn.computeAndUpdateOuterECHExtension(inner, ech, true)
uconn.echCtx = ech
return nil

View file

@ -578,3 +578,21 @@ func (c *UConn) clientHandshake(ctx context.Context) (err error) {
}
return nil
}
func (c *UConn) echTranscriptMsg(outer *clientHelloMsg, echCtx *echClientContext) (err error) {
encodedInner, err := encodeInnerClientHelloReorderOuterExts(echCtx.innerHello, int(echCtx.config.MaxNameLength), c.extensionsList())
if err != nil {
return err
}
decodedInner, err := decodeInnerClientHello(outer, encodedInner)
if err != nil {
return err
}
if err := transcriptMsg(decodedInner, echCtx.innerTranscript); err != nil {
return err
}
return nil
}