From 5893e1ec282031cf7cc61d64c219540ab4626080 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Fri, 31 Jan 2020 12:19:58 +0700 Subject: [PATCH] add a workaround for the ChaCha20 bug --- internal/handshake/header_protector.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/internal/handshake/header_protector.go b/internal/handshake/header_protector.go index 019d57036..795307390 100644 --- a/internal/handshake/header_protector.go +++ b/internal/handshake/header_protector.go @@ -3,6 +3,7 @@ package handshake import ( "crypto/aes" "crypto/cipher" + "crypto/rand" "fmt" "github.com/marten-seemann/chacha20" @@ -91,10 +92,27 @@ func newChaChaHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte } func (p *chachaHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { + // Workaround for https://github.com/lucas-clemente/quic-go/issues/2326. + // The ChaCha20 implementation panics when the nonce is 0xffffffff. + // Don't apply header protection in that case. + // The packet will end up undecryptable, but it only applies to 1 in 2^32 packets. + if sample[0] == 0xff && sample[1] == 0xff && sample[2] == 0xff && sample[3] == 0xff { + return + } p.apply(sample, firstByte, hdrBytes) } func (p *chachaHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { + // Workaround for https://github.com/lucas-clemente/quic-go/issues/2326. + // The ChaCha20 implementation panics when the nonce is 0xffffffff. + // Apply header protection with a random mask, in order to not leak any data. + // The packet will end up undecryptable, but this only applies to 1 in 2^32 packets. + if sample[0] == 0xff && sample[1] == 0xff && sample[2] == 0xff && sample[3] == 0xff { + if _, err := rand.Read(p.mask[:]); err != nil { + panic("couldn't get rand for ChaCha20 bug workaround") + } + p.applyMask(firstByte, hdrBytes) + } p.apply(sample, firstByte, hdrBytes) } @@ -107,7 +125,10 @@ func (p *chachaHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes [ } copy(p.sampleBuf[:], sample) chacha20.XORKeyStream(p.mask[:], p.mask[:], &p.sampleBuf, &p.key) + p.applyMask(firstByte, hdrBytes) +} +func (p *chachaHeaderProtector) applyMask(firstByte *byte, hdrBytes []byte) { if p.isLongHeader { *firstByte ^= p.mask[0] & 0xf } else {